# include"ppForm.h"
# include<iostream>
# include<cmath>
# include<fstream>

using namespace std;

// 对数函数类 ln(x)
class Ln: public Function
{
public:
    double operator() (const double & x) const
    {
        if(x <= 0)
        {
            cout << "log cannot apply to x <= 0." << endl;
            return 0;
        }
        return (log(x));
    }

    double diff(const double & _x) const
    {
        if(_x <= 0)
        {
            cout << "log cannot apply to x <= 0." << endl;
            return 0;
        }

        return (1.0 / _x);
    }

    double second_diff(const double & _x) const
    {
        if(_x <= 0)
        {
            cout << "log cannot apply to x <= 0." << endl;
            return 0;
        }

        return (- 1.0 / (_x * _x));
    }
};

// 三角函数类 sin(x)
class Sin: public Function
{
public:
    double operator() (const double & x) const
    {
        return (sin(x));
    }

    double diff(const double & _x) const
    {
        return cos(_x);
    }
};

// 对 ppForm 类的测试程序
int main()
{
    // Test for sorting the knots.
    cout << "Test for sorting." << endl;
    struct point a1{1,2}, a2{-1,9}, a3{0,4}, a4{3,2};
    vector<struct point> vec{a1, a2, a3, a4};
    for (int i = 0; i < vec.size(); i++)
    {
        cout << vec[i].x << "," << vec[i].y << " ";
    }
    cout << endl;
    ppForm pptest1(S01, vec);
    pptest1.PrintPoint();

    // Test for refusing the wrong input.
    cout << "Test for wrong input." << endl;
    struct point a5{1,3};
    vec.push_back(a5);
    for (int i = 0; i < vec.size(); i++)
    {
        cout << vec[i].x << "," << vec[i].y << " ";
    }
    cout << endl;
    ppForm pptest2(S01, vec);

    // Tese for judging whether _x can be calculate.
    cout << "Test for the domain." << endl;
    cout << "s(2) = " << pptest1.solve(2) << endl;
    cout << "s(4) = " << pptest1.solve(4) << endl;

    // Test the function
    cout << "Test for function." << endl;
    // Define ln(x) and the data
    Ln fln;
    vector<double> coe1{1, 2, 3, 4, 6};
    double eps1 = (6.0 - 1.0) / 200;

    // Test S01 (Use ln(x))
    ppForm a01(S01, coe1, fln);
    ofstream fout01;
    fout01.open("data/ppForm01.txt", ios::out);
    for(int j = 0; j < 201; j++)
    {
        fout01 << a01.solve(1 + j * eps1) << " ";
    }
    fout01 << endl;
    fout01.close();

    cout << "Finish testing S01." << endl;

    // Test S32 Complete (Use ln(x))
    ppForm a32Complete(S32, coe1, fln, Complete);
    ofstream fout32Complete;
    fout32Complete.open("data/ppForm32Complete.txt", ios::out);
    for(int j = 0; j < 201; j++)
    {
        fout32Complete << a32Complete.solve(1 + j * eps1) << " ";
    }
    fout32Complete << endl;
    fout32Complete.close();

    cout << "Finish testing Complete." << endl;

    // Test S32 Specified (Use ln(x))
    ppForm a32Specified(S32, coe1, fln, Specified);
    ofstream fout32Specified;
    fout32Specified.open("data/ppForm32Specified.txt", ios::out);
    for(int j = 0; j < 201; j++)
    {
        fout32Specified << a32Specified.solve(1 + j * eps1) << " ";
    }
    fout32Specified << endl;
    fout32Specified.close();

    cout << "Finish testing Specified." << endl;

    // Test S32 Not a Knot (Use ln(x))
    ppForm a32NotAKnot(S32, coe1, fln, NotAKnot);
    ofstream fout32NotAKnot;
    fout32NotAKnot.open("data/ppForm32NotAKnot.txt", ios::out);
    for(int j = 0; j < 201; j++)
    {
        fout32NotAKnot << a32NotAKnot.solve(1 + j * eps1) << " ";
    }
    fout32NotAKnot << endl;
    fout32NotAKnot.close();

    cout << "Finish testing Not a knot." << endl;


    // Define sin(x) and the data
    Sin fsin;
    vector<double> coe2{0, M_PI/4, M_PI/2, M_PI, 1.5 * M_PI, 1.25 * M_PI, 1.75 * M_PI, 2*M_PI};
    double eps2 = 2 * M_PI / 201;

    // Test S32 Nature (Use sin(x))
    ppForm a32Nature(S32, coe2, fsin, Nature);
    ofstream fout32Nature;
    fout32Nature.open("data/ppForm32Nature.txt", ios::out);
    for(int j = 0; j < 201; j++)
    {
        fout32Nature << a32Nature.solve(j * eps2) << " ";
    }
    fout32Nature << endl;
    fout32Nature.close();

    cout << "Finish testing Nature." << endl;

    // Test S32 Periodic (Use sin(x))
    ppForm a32Periodic(S32, coe2, fsin, Periodic);
    ofstream fout32Periodic;
    fout32Periodic.open("data/ppForm32Periodic.txt", ios::out);
    for(int j = 0; j < 201; j++)
    {
        fout32Periodic << a32Periodic.solve(j * eps2) << " ";
    }
    fout32Periodic << endl;
    fout32Periodic.close();

    cout << "Finish testing Periodic." << endl;
    return 0;
}